/*
*/

#include "learning.h"

#include "beam.h"
#include "display.h"
#include "event.h"
#include "form.h"
#include "timer.h"

#define	DEBUG

#define	CNT_MASK	0x3fffff
#define	CNT_SHIFT	22
#define	FORM_LEN	512
#define	FORM_END	0
#define	SIGNAL_CCR	CCR2
#define	SIGNAL_DMA	DMA1_Channel7
#define	SIGNAL_TIM	TIM2
#define	SLOT_BURR	1
#define	SLOT_LO		2
#define	SLOT_LO1	3
#define	SLOT_LO2	4
#define	SLOT_LO3	5
#define	SLOT_HI		6
#define	SLOT_HI1	7
#define	SLOT_HI2	8
#define	SLOT_HI3	9
#define	SLOT_LEN	10

typedef struct _SLOT{
    u16	count;
    u16	factor;
    u16	time;
    u16	period;
}SLOT;

static u32*	Form	(u32,u32,u16*,u32,u32*,u32*);
static void	Slot	(SLOT*,u32,SLOT*);
static void	Stop	(void);

static u32	form[FORM_LEN];

static u32 BeamIn(u32 dmo){
    u32	*dst,*dsx;
    u32	dmc,evn,t_m;
    s32	tmc;
    
    if((dmc=DMA_GetCurrDataCounter(SIGNAL_DMA))==dmo)
	return 0;
    t_m=Timer();
    dst=form;
    dsx=dst+(FORM_LEN-1);
    tmc=form[0]=1;
    while(dst){
	if(dmc!=dmo){
	    dst=Form(dmc,dmo,signal.wave,tmc,dst,dsx);
	    dmo=dmc;
	    tmc=0;
	}
	evn=WaitEvent();
	tmc++;
	if((evn&EVN_MASK)==EVN_KEYRELEASE)
	    BeamOff();
	if(TimeOut(t_m,400)){
	    *++dst=0;
	    dst=0;
	}
	dmc=DMA_GetCurrDataCounter(SIGNAL_DMA);
    }
    return 1;
}

static void Fact(u32 *src,BEAM *dst){
    u16	 *d;
    u32	 b,f,m,*s,v;
    SLOT o[SLOT_LEN],*t,*x;
    
    o->count=
	o->factor=
	    o->time=
		o->period=0;
    for(t=o+1,x=o+SLOT_LEN;t<x;t++)
	*t=*o;
    
    for(s=src;v=*s;s++){
	if(m=v>>CNT_SHIFT){
	    v&=CNT_MASK;
	    o->count+=m+1;
	    o->time+=v;
	    o->period=(o->time<<8)/o->count;
	    Slot(o+SLOT_HI,v,o);
	}
	else Slot(o+SLOT_LO,v,o);
    }
    o->period=(o->time<<8)/o->count;
    f=o->time/o->count;
    b=0xffff;
    for(t=o+SLOT_LO,x=o+SLOT_LEN;t<x;t++){
	if(t->time){
	    v=(t->time-f)/t->count;
	    t->period=v;
	    if(b>v)
		b=v;
	}
    }
    if(b!=0xffff){
#ifdef	DEBUG
	for(t=o+SLOT_LO,x=o+SLOT_LEN;t<x;t++)
	    t->factor=((t->period<<8)/b+26)>>8;
	d=dst->wave;
	for(m=0;m<BEAM_LEN;m++)
	    *d++=0;
#endif
	dst->period=o->period;
	dst->repetition=(b<<8)/o->period;
	d=dst->wave;
	for(s=src;v=*s;s++)if(v>f){
	    m=v&(~CNT_MASK)?0x8000:0;
	    v&=CNT_MASK;
	    *d++=((((m?v+f:v-f)<<8)/b+26)>>8)|m;
	}
	*d=0;
    }
}

static u32* Form(u32 dmc,u32 dmo,u16 *buf,u32 xtr,u32 *dst,u32 *dsx){
    u16	*src,*end,*wax;
    s32	dlt,pcr,ppr;
    u32	cnt,tim,ttm,vcr,vpr;
    
    wax=buf+BEAM_LEN;
    src=wax-dmo;
    vcr=*dst;
    cnt=vcr>>CNT_SHIFT;
    tim=vcr&CNT_MASK;
    ppr=cnt?tim/(cnt+1):tim;
    if(ppr){
	end=src-1;
	if(end<buf)
	    end=wax-1;
	vpr=*end;
    }
    else{
	cnt=0xfffffffe;		//-2
	vpr=*src;
    }
    end=wax-dmc;
    dst--;
    while(src!=end){
	vcr=*src++;
	if(src>=wax)
	    src=buf;
	if(vcr==vpr)
	    pcr=dlt=0;
	else{
	    pcr=(vcr-vpr)&0xffff;
	    dlt=ppr?ppr-pcr:0;
	}
	if(dlt>2||dlt<-2){
	    *++dst=(cnt<<CNT_SHIFT)+tim;
	    if(dst>=dsx){
		*dst=0;
		return 0;
	    }
	    cnt=0;
	    tim=pcr;
	    ttm+=pcr;
	}
	else{
	    tim+=pcr;
	    ttm+=pcr;
	    cnt++;
	}
	if(tim>8000){
	    *++dst=0;
	    return 0;
	}
	ppr=pcr;
	vpr=vcr;
    }
    *++dst=(cnt<<CNT_SHIFT)+tim;
    return dst;
}

static u32 Press(u32 evn,u32 stt){
    u32	key;
    if((key=GetKey(evn))==KEY_PROG){
	IndicatorOn();
	return STT_NIL;
    }
    BeamOn(key);
    do evn=WaitEvent();
    while(evn!=EVN_KEYRELEASE);
    BeamOff();
    return stt;
}

static void Slot(SLOT *slt,u32 tim,SLOT *carrier){
    u32	 d,e,f;
    SLOT *b,*o,*q,*x;
    
    f=carrier->period>>7;
    if(tim<f){
	b=carrier+1;
	b->time+=tim;
	b->count++;
	b->period=(b->time<<8)/b->count;
	return;
    }
    d=tim-f;
    e=tim+f;
    for(x=slt+4;slt<x;slt++){
	if(d>slt->period){
	    for(q=x-1;q>slt;q--){
		o=q-1;
		if(o->period){
		    q->period=o->period;
		    q->time+=o->time;
		    q->count+=o->count;
		}
		else break;
	    }
	    slt->period=tim;
	    slt->time=tim;
	    slt->count=1;
	    return;
	}
	else if(e>slt->period){
	    slt->period=tim;
	    slt->time+=tim;
	    slt->count++;
	    return;
	}
    }
}

static u32 Start(void){
    TIM_TimeBaseInitTypeDef	b;
    TIM_ICInitTypeDef           i;
    DMA_InitTypeDef             d;
    
    b.TIM_Period	=0xffff;
    b.TIM_Prescaler	=144;
    b.TIM_ClockDivision	=0;
    b.TIM_CounterMode	=TIM_CounterMode_Up;
    TIM_TimeBaseInit(SIGNAL_TIM,&b);
    i.TIM_Channel	=TIM_Channel_2;
    i.TIM_ICPolarity	=TIM_ICPolarity_Rising;
    i.TIM_ICSelection	=TIM_ICSelection_DirectTI;
    i.TIM_ICPrescaler	=TIM_ICPSC_DIV1;
    i.TIM_ICFilter	=0;
    TIM_ICInit(SIGNAL_TIM,&i); 
    TIM_ClearITPendingBit(SIGNAL_TIM,TIM_IT_CC2);
    d.DMA_PeripheralBaseAddr=(u32)&SIGNAL_TIM->SIGNAL_CCR;
    d.DMA_MemoryBaseAddr=(u32)signal.wave;
    d.DMA_BufferSize	=BEAM_LEN;
    d.DMA_DIR		=DMA_DIR_PeripheralSRC;
    d.DMA_PeripheralInc	=DMA_PeripheralInc_Disable;
    d.DMA_MemoryInc	=DMA_MemoryInc_Enable;
    d.DMA_PeripheralDataSize=DMA_PeripheralDataSize_HalfWord;
    d.DMA_MemoryDataSize=DMA_MemoryDataSize_HalfWord;
    d.DMA_Mode		=DMA_Mode_Circular;
    d.DMA_Priority	=DMA_Priority_High;
    d.DMA_M2M		=DMA_M2M_Disable;
    DMA_Init(SIGNAL_DMA,&d);
    DMA_Cmd(SIGNAL_DMA,ENABLE);
    TIM_DMACmd(SIGNAL_TIM,TIM_DMA_CC2,ENABLE);
    TIM_Cmd(SIGNAL_TIM,ENABLE);
    return DMA_GetCurrDataCounter(SIGNAL_DMA);
}

static void Stop(void){
    DMA_Cmd(SIGNAL_DMA,DISABLE);
    TIM_Cmd(SIGNAL_TIM,DISABLE);
}

//------------------------------------------------------------------------------

u32 Learning(u32 stt){
    u32	evn,dmo,t_m;
    
    FlashOn();
    t_m=Timer();
    dmo=Start();
    while(stt>=STT_PRESET){
	evn=WaitEvent();
	if((evn&EVN_MASK)==EVN_KEYPRESS){
	    Stop();
	    if(stt=Press(evn,stt)){
		dmo=Start();
		t_m=Timer();
	    }
	}
	if(TimeOut(t_m,TIM_OUT)){
	    Stop();
	    IndicatorOff();
	    stt=STT_NIL;
	}
	if(BeamIn(dmo)){
	    Stop();
	    Fact(form,&signal);
	    stt=STT_SIGNAL;
	}
    }
    return stt;
}

